# -*- coding: utf-8 -*-
# @Time    : 2024/7/16 11:28
# @Author  : yujiahao
# @File    : 32_fastapi_safety_create_user_model.py
# @description:获取当前用户


from typing import Union

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


# todo 1、创建用户模型
class User(BaseModel):
    '''
    创建Pydantic用户模型，与使用Pydantic声明请求体相同，并且可以在任何位置使用
    '''
    username: str
    email: Union[str, None] = None
    full_name: Union[str, None] = None
    disabled: Union[bool, None] = None


def fake_decode_token(token):
    return User(
        username=token + "fakedecoded", email="john@example.com", full_name="John Doe"
    )


# todo 2、创建 get_current_user 依赖项
# get_current_user 使用 oauth2_scheme 作为依赖项。
# 与之前直接在路径操作中的做法相同，新的 get_current_user 依赖项从子依赖项 oauth2_scheme 中接收 str 类型的 token


# todo 3、获取用户
# get_current_user 使用创建的（伪）工具函数，该函数接收 str 类型的令牌，并返回 Pydantic 的 User 模型

async def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    return user


# todo 4、注入当前用户
# 在路径操作 的 Depends 中使用 get_current_user
# 注意，此处把 current_user 的类型声明为 Pydantic 的 User 模型。
# 这有助于在函数内部使用代码补全和类型检查。
# 依赖系统的这种设计方式可以支持不同的依赖项返回同一个 User 模型。
# 而不是局限于只能有一个返回该类型数据的依赖项。
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user


"""
其它模型：
-----------
1. 直接在路径操作函数中获取当前用户，并用 Depends 在依赖注入系统中处理安全机制。
2. 开发者可以使用任何模型或数据满足安全需求（本例中是 Pydantic 的 User 模型）。
3. 不局限于只能使用特定的数据模型、类或类型：
    - 可以使用 id 和 email 替代 username。
    - 可以使用字符串、字典，甚至是数据库类模型的实例。
    - 登录应用的可以是用户，或是只拥有访问令牌的机器人、程序或其它系统。
4. FastAPI 通过依赖注入系统支持各种模型、类、数据库。

代码大小：
----------
1. 示例代码看起来有些冗长，因为它同时包含了安全、数据模型的工具函数，以及路径操作等代码。
2. 关键在于：
    - 安全和依赖注入的代码只需要写一次。
    - 就算写得再复杂，也只是在一个位置写一次就够了。
3. 所有端点（路径操作）都可以使用同一个安全系统，无论有多少个端点。
4. 所有路径操作只需 3 行代码就可以了：
    - 定义路径操作函数。
    - 使用 Depends 声明依赖项。
    - 处理请求并返回响应。
    
【小结】
    - 现在，我们可以直接在路径操作函数中获取当前用户。

    - 至此，安全的内容已经讲了一半。

    - 只要再为用户或客户端的路径操作添加真正发送 username 和 password 的功能就可以了。
"""
